/*******************************************************************************
 * Copyright (c) 2010 European Software Institute - Tecnalia.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Author - Adri�n Noguero (adrian.noguero@esi.es)
 *     
 *******************************************************************************/
package es.esi.gemde.vv.masttool.constraints;

import java.util.HashMap;
import java.util.List;
import java.util.Vector;

import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;

import es.esi.gemde.modelvalidator.service.IModelValidatorConstraint;
import es.esi.gemde.vv.mast.mastmodel.Activity;
import es.esi.gemde.vv.mast.mastmodel.Bursty_External_Event;
import es.esi.gemde.vv.mast.mastmodel.Immediate_Ceiling_Resource;
import es.esi.gemde.vv.mast.mastmodel.MAST_MODEL;
import es.esi.gemde.vv.mast.mastmodel.ModelPackage;
import es.esi.gemde.vv.mast.mastmodel.Periodic_External_Event;
import es.esi.gemde.vv.mast.mastmodel.Regular_Transaction;
import es.esi.gemde.vv.mast.mastmodel.Simple_Operation;
import es.esi.gemde.vv.mast.mastmodel.Singular_External_Event;
import es.esi.gemde.vv.mast.mastmodel.Sporadic_External_Event;
import es.esi.gemde.vv.mast.mastmodel.Unbounded_External_Event;
import es.esi.gemde.vv.masttool.MastToolPlugin;
import es.esi.gemde.vv.masttool.resources.TreeList;
import es.esi.gemde.vv.masttool.resources.utils.MASTModelingUtils;

/**
 * Java implementation of the MAST Shared Resource Check 5
 *
 * @author Adrian Noguero (adrian.noguero@tecnalia.com)
 * @version 1.0
 * @since 1.0
 *
 */
public class SharedResourcesCheck05 implements IModelValidatorConstraint {

	// Constants
	private static final String CATEGORY = "MAST Shared Resources Checks";
	private static final String NAME = "05 - Global shared resources are Immediate Ceiling Resources";
	
	/* (non-Javadoc)
	 * @see es.esi.gemde.modelvalidator.service.IModelValidatorConstraint#getApplicationContext()
	 */
	@Override
	public EClass getApplicationContext() {
		return ModelPackage.eINSTANCE.getMAST_MODEL();
	}

	/* (non-Javadoc)
	 * @see es.esi.gemde.modelvalidator.service.IModelValidatorConstraint#getApplicationMetamodel()
	 */
	@Override
	public EPackage getApplicationMetamodel() {
		return ModelPackage.eINSTANCE.getMAST_MODEL().getEPackage();
	}

	/* (non-Javadoc)
	 * @see es.esi.gemde.modelvalidator.service.IModelValidatorConstraint#getBody()
	 */
	@Override
	public String getBody() {
		// Not used
		return "Constraint implemented in Java";
	}

	/* (non-Javadoc)
	 * @see es.esi.gemde.modelvalidator.service.IModelValidatorConstraint#getCategory()
	 */
	@Override
	public String getCategory() {
		return CATEGORY;
	}

	/* (non-Javadoc)
	 * @see es.esi.gemde.modelvalidator.service.IModelValidatorConstraint#getName()
	 */
	@Override
	public String getName() {
		return NAME;
	}

	/* (non-Javadoc)
	 * @see es.esi.gemde.modelvalidator.service.IModelValidatorConstraint#getSeverity()
	 */
	@Override
	public int getSeverity() {
		return IStatus.ERROR;
	}

	/* (non-Javadoc)
	 * @see es.esi.gemde.modelvalidator.service.IModelValidatorConstraint#isProtected()
	 */
	@Override
	public boolean isProtected() {
		return true;
	}

	/* (non-Javadoc)
	 * @see es.esi.gemde.modelvalidator.service.IModelValidatorConstraint#targetsTypeOf(org.eclipse.emf.ecore.EObject)
	 */
	@Override
	public boolean targetsTypeOf(EObject target) {
		return (target instanceof MAST_MODEL);
	}

	/* (non-Javadoc)
	 * @see es.esi.gemde.modelvalidator.service.IModelValidatorConstraint#validate(org.eclipse.emf.ecore.EObject)
	 */
	@Override
	public IStatus validate(EObject onTarget) {
if (onTarget instanceof MAST_MODEL) {
			
			MAST_MODEL model = (MAST_MODEL)onTarget;
			
			Vector<Object> globalSharedResList = new Vector<Object>();
			HashMap<String, String> servers = new HashMap<String, String>();
			
			for (Regular_Transaction t : model.getRegular_Transaction()) {
			
				// Get the external event list, as they are the inputs of the graph
				Vector<String> externalEvents = new Vector<String>();
				for (Bursty_External_Event e : t.getBursty_External_Event()) {
					externalEvents.add(e.getName());
				}
				for (Periodic_External_Event e : t.getPeriodic_External_Event()) {
					externalEvents.add(e.getName());
				}
				for (Singular_External_Event e : t.getSingular_External_Event()) {
					externalEvents.add(e.getName());
				}
				for (Sporadic_External_Event e : t.getSporadic_External_Event()) {
					externalEvents.add(e.getName());
				}
				for (Unbounded_External_Event e : t.getUnbounded_External_Event()) {
					externalEvents.add(e.getName());
				}
				
				
				// For each event we create a graph using a Tree
				for (String e : externalEvents) {
					TreeList<Object> tree = MASTModelingUtils.createTree(e, t); 
					if (tree != null) {
						// For each transaction path we check the usage of the resources
						for (Object[] path : MASTModelingUtils.getPaths(tree)) {
							// We create a map between resources and number of uses
							
							for (Object node : path) {
								if (node instanceof Activity) {
									Object op = MASTModelingUtils.getModelOperation(((Activity)node).getActivity_Operation(), model);
									if (op instanceof Simple_Operation) {
										if (((Simple_Operation)op).getShared_Resources_To_Lock() != null) {
											for (String res : ((Simple_Operation)op).getShared_Resources_To_Lock()) {
												if (servers.get(res) == null) {
													servers.put(res, ((Activity)node).getActivity_Server());
												}
												// Then if this happens it is a global shared resource
												else if (servers.get(res) != ((Activity)node).getActivity_Server() && MASTModelingUtils.getModelSchedulingServer(servers.get(res), model).getScheduler() != MASTModelingUtils.getModelSchedulingServer(((Activity)node).getActivity_Server(), model).getScheduler()) {
													globalSharedResList.add(MASTModelingUtils.getModelResource(res, model));
												}
											}
										}
										if (((Simple_Operation)op).getShared_Resources_List() != null) {
											for (String res : ((Simple_Operation)op).getShared_Resources_List()) {
												if (servers.get(res) == null) {
													servers.put(res, ((Activity)node).getActivity_Server());
												}
												// Then if this happens it is a global shared resource
												else if (servers.get(res) != ((Activity)node).getActivity_Server() && MASTModelingUtils.getModelSchedulingServer(servers.get(res), model) != null && MASTModelingUtils.getModelSchedulingServer(servers.get(res), model).getScheduler() != null && MASTModelingUtils.getModelSchedulingServer(((Activity)node).getActivity_Server(), model) != null && MASTModelingUtils.getModelSchedulingServer(((Activity)node).getActivity_Server(), model).getScheduler() != null && !MASTModelingUtils.getModelSchedulingServer(servers.get(res), model).getScheduler().equals(MASTModelingUtils.getModelSchedulingServer(((Activity)node).getActivity_Server(), model).getScheduler())) {
													globalSharedResList.add(MASTModelingUtils.getModelResource(res, model));
												}
											}
										}
									}
									else if (op != null) {
										List<Simple_Operation> enclosedOps = MASTModelingUtils.getComposingOps(model, op);
										if (enclosedOps != null) {
											for (Simple_Operation simple : enclosedOps) {
												if (simple.getShared_Resources_To_Lock() != null) {
													for (String res : simple.getShared_Resources_To_Lock()) {
														if (servers.get(res) == null) {
															servers.put(res, ((Activity)node).getActivity_Server());
														}
														// Then if this happens it is a global shared resource
														else if (servers.get(res) != ((Activity)node).getActivity_Server() && MASTModelingUtils.getModelSchedulingServer(servers.get(res), model).getScheduler() != MASTModelingUtils.getModelSchedulingServer(((Activity)node).getActivity_Server(), model).getScheduler()) {
															globalSharedResList.add(MASTModelingUtils.getModelResource(res, model));
														}
													}
												}
												if (simple.getShared_Resources_List() != null) {
													for (String res : simple.getShared_Resources_List()) {
														if (servers.get(res) == null) {
															servers.put(res, ((Activity)node).getActivity_Server());
														}
														// Then if this happens it is a global shared resource
														else if (servers.get(res) != ((Activity)node).getActivity_Server() && MASTModelingUtils.getModelSchedulingServer(servers.get(res), model).getScheduler() != MASTModelingUtils.getModelSchedulingServer(((Activity)node).getActivity_Server(), model).getScheduler()) {
															globalSharedResList.add(MASTModelingUtils.getModelResource(res, model));
														}
													}
												}
											}
										}
									}
								}
							}
						}
					}
				}
			}
			
			for (Object res : globalSharedResList) {
				if (!(res instanceof Immediate_Ceiling_Resource)) {
					return new Status(IStatus.ERROR, MastToolPlugin.PLUGIN_ID, "Resource " + res + " is used used globally and it's not an Immediate Ceiling Resource");
				}
			}
			
		}
		return new Status(IStatus.OK, MastToolPlugin.PLUGIN_ID, null);
	}

	@Override
	public String getDescription() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getIdentifier() {
		// TODO Auto-generated method stub
		return null;
	}
	
	@Override
	public ResourceType getResourceType() {
		return ResourceType.PROTECTED;
	}

}
